﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace IceCS.HotLoad.Modules.Interfaces
{
    /// <summary>
    /// 热加载管理器
    /// </summary>
    public class HotLoadManager
    {
        /// <summary>
        /// 程序集、接口和实现类列表集合
        /// </summary>
        public Dictionary<string, Dictionary<string, List<Type>>> AssemblySets { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public HotLoadManager()
        {
            AssemblySets = new Dictionary<string, Dictionary<string, List<Type>>>();
        }
        /// <summary>
        /// 加载dll
        /// </summary>
        /// <param name="assemblyFile">程序集文件的路径，以此dll所在目录为根目录进行查找</param>
        /// <param name="overrideExistAssembly">是否覆盖已加载的程序集</param>
        /// <returns></returns>
        public bool LoadDll(string assemblyFile, bool overrideExistAssembly = true)
        {
            //程序集 类 
            try
            {
                //使用字节流的方式加载程序集，避免数据集文件被占用无法替换
                var assemblyFilePath = Path.Combine(Environment.CurrentDirectory, assemblyFile);
                byte[] rawAssembly;
                using (var fs = File.OpenRead(assemblyFile))
                {
                    using (var br = new BinaryReader(fs))
                    {
                        rawAssembly = br.ReadBytes((int)br.BaseStream.Length);
                    }
                }
                var assembly = Assembly.Load(rawAssembly);
                var assemblyFullName = assembly.FullName.Split(",")[0];
                if (AssemblySets.ContainsKey(assemblyFullName))
                {
                    //已存在则不覆盖
                    if (!overrideExistAssembly)
                    {
                        return true;
                    }
                    //以程序集为单位进行覆盖
                    AssemblySets.Remove(assemblyFullName);
                    GC.Collect();
                }

                AssemblySets.Add(assemblyFullName, new Dictionary<string, List<Type>>());
                //接口 实现类集合
                var interfaceSets = AssemblySets[assemblyFullName];
                //获取程序集里面的公共类
                Type[] publicClasses = assembly.GetExportedTypes();
                foreach (var publicClasse in publicClasses)
                {
                    //获取当前类实现的接口列表
                    Type[] impInterfaces = publicClasse.GetInterfaces();
                    foreach (var impInterface in impInterfaces)
                    {
                        var interfaceName = impInterface.FullName;
                        if (!interfaceSets.ContainsKey(interfaceName))
                        {
                            interfaceSets.Add(interfaceName, new List<Type>());
                        }
                        interfaceSets[interfaceName].Add(publicClasse);
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }
       
        /// <summary>
        /// 根据指定接口和条件创建实例
        /// </summary>
        /// <typeparam name="T">接口类</typeparam>
        /// <param name="predicate">判断用接口的哪一个实现</param>
        /// <param name="assemblyFullName">可选，指定哪个程序集里面的实现</param>
        /// <returns></returns>
        public T CreateInstance<T>(Func<Type, bool> predicate, string assemblyFullName = "")
        {
            IEnumerable<string> assemblyNames = AssemblySets.Keys;
            if (!string.IsNullOrWhiteSpace(assemblyFullName))
            {
                assemblyNames = assemblyNames.Where(n => n == assemblyFullName);
            }
            foreach (var assemblyName in assemblyNames)
            {
                var interfaceSets = AssemblySets[assemblyName];
                var key = typeof(T).FullName;
                if (!interfaceSets.ContainsKey(key))
                {
                    throw new TypeLoadException("指定的接口没有找到对应的实现");
                }
                var classList = interfaceSets[key];
                for (int i = 0; i < classList.Count; i++)
                {
                    var classType = classList[i];
                    if (predicate(classType))
                    {
                        return (T)Activator.CreateInstance(classType);
                    }
                }
            }
            throw new TypeLoadException("指定的接口没有找到对应的实现");
        }
    }
}
